/*
 * @Author: 肖思汗 
 * @Date: 2019-05-08 01:16:01 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-11-24 11:18:27
 */
//
import elementResizeEvent from "element-resize-event";
import React, { Component, CSSProperties } from "react";
import ReactDOM from "react-dom";
import * as THREE from "three";
import { Scene } from "three";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";
import { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera.js";
import { LoadingManager } from "three/src/loaders/LoadingManager.js";
import { TextureLoader } from "three/src/loaders/TextureLoader.js";
import { WebGLRenderer } from "three/src/renderers/WebGLRenderer.js";
import TitleDom from "../components/TitleDom/TitleDom";
import TitleDomControl from "../components/TitleDom/TitleDomController";
import Material_mixin from "../mixin/Material_mixin";
import Object3D_mixin from "../mixin/Object3D_mixin";
import SkinnedMesh_mixin from "../mixin/SkinnedMesh_mixin";
import Object3D from "../Object3D/Object3D";
import xrt_util from "../xrt_util/xrt_util";
import CollideCubeInstanceMesh from "./CollideCubeInstanceMesh";
import CollideCylindricalInstanceMesh from "./CollideCylindricalInstanceMesh";
import CollidePlaneInstanceMesh from "./CollidePlaneInstanceMesh";
import Composer from "./composer/Composer";
import Emit from "./Emit";
// import GifTextureLoader from "./loaders/GifTextureLoader.js";
import reactThreeDevTool from "./reactThreeDevTool/reactThreeDevTool";
import THREEMouse from "./THREEMouse";
import { Object3DEvent } from "./type";
import isMobile from "is-mobile";

THREE.Cache.enabled = true; // 开启全局的 缓存

Object3D_mixin();

Material_mixin();

SkinnedMesh_mixin();

/**
* @param dev     @非开发模式下  会自动去除 参考线等
* @param shadowMapEnabled     @开启阴影
* @param shadowMapType     @阴影类型
*
* @export
* @class Renderer
*/

interface RendererProps {
    className?: string;
    style?: CSSProperties;
    dev?: boolean; // 开发模式
    shadowMapEnabled?: boolean; //开启阴影
    shadowMapType?: THREE.ShadowMapType  //阴影类型
    alpha?: boolean, // 透明背景
    onBeforeRender?: (renderer: WebGLRenderer, camera: PerspectiveCamera, scene: Scene) => void;
    onAfterRender?: (renderer: WebGLRenderer, camera: PerspectiveCamera, scene: Scene) => void;
    onResize?: (width?: number, height?: number) => void;
    onStart?: (url: string, loaded: number, total: number) => void // 加载进度
    onProgress?: (url: string, loaded: number, total: number) => void // 加载进度
    onLoad?: () => void, // 加载完成
    needRender?: () => boolean;
}

// 渲染器上下文
export const ReactThreeContext = React.createContext({});

export default class Renderer extends Component<RendererProps> {
    constructor(props: RendererProps) {
        super(props);
    }

    static defaultProps = {
        dev: true, // 非开发模式下  会自动去除 参考线等
        shadowMapEnabled: true, // 开启阴影
        shadowMapType: THREE.BasicShadowMap, // 阴影类型
        onLoad: () => { }, // 加载完成
        onProgress: () => { } // 加载进度
    }

    isMobile = isMobile();

    // 组件是否卸载了
    isUnMoment = false;

    // 所有需要鼠标拾取的模型对象都放到这里面
    mouseEventMesh: Object3D[] = [];

    // 当前相机类型
    camera = new PerspectiveCamera(50, 1, 1, 2000); //创建透视相机

    // 音频监听器
    audioListener?: AudioListener;

    // 创建音频监听器
    creatAudioListener() {
        this.audioListener = new AudioListener();
        // this.audioListener.matrixAutoUpdate = false;
        // this.audioListener.updateMatrix();
        // this.perspectiveCamera.add(this.audioListener); // 把音频监听器放到相机对象里面
    }

    renderer: WebGLRenderer = (() => { // 创建渲染器

        const renderer = new WebGLRenderer({
            alpha: true,// 透明背景
            antialias: true, // 阴影抗锯齿
            precision: "highp", //precision:highp/mediump/lowp着色精度选择
            // logarithmicDepthBuffer: true, // 对数深度缓冲区 避免当2个面完全重合时闪烁
            preserveDrawingBuffer: false, // 是否保存绘图缓冲
            stencil: false, // 模版
        });

        renderer.setPixelRatio(window.devicePixelRatio);

        renderer.localClippingEnabled = true;

        const {
            shadowMapEnabled = false,
            shadowMapType = THREE.BasicShadowMap
        } = this.props;

        //阴影开启
        renderer.shadowMap.enabled = shadowMapEnabled;

        //阴影类型
        renderer.shadowMap.type = shadowMapType;

        //不自动清理
        renderer.autoClear = false;

        renderer.domElement.style.position = "absolute";
        renderer.domElement.style.left = "0px";
        renderer.domElement.style.top = "0px";

        return renderer;
    })();

    // 碰撞检测专用的立方体实例模型
    collideCubeInstanceMesh = new CollideCubeInstanceMesh();

    // 碰撞检测专用的圆柱体实例模型
    collideCylindricalInstanceMesh = new CollideCylindricalInstanceMesh();

    // 碰撞检测专用的面实例模型
    collidePlaneInstanceMesh = new CollidePlaneInstanceMesh();

    // 创建场景对象
    scene: THREE.Scene = (() => {
        const scene = new THREE.Scene(); // 场景
        scene.matrixAutoUpdate = false;
        scene.autoUpdate = false;
        // scene.updateMatrix();
        return scene;
    })();

    // 鼠标事件对象
    mouseEvent = new THREEMouse(this.renderer, this.camera, this.scene);

    //创建后期处理 合成器
    composer = new Composer(this.renderer);

    emiter = new Emit();

    TitleDomController = new TitleDomControl();

    // 加载管理器
    loadingManager = (() => {

        const { onStart, onProgress, onLoad } = this.props;

        const manager = new LoadingManager();

        manager.onStart = (url: string, loaded: number, total: number) => {
            onStart && onStart(url, loaded, total);
        }

        manager.onProgress = (url: string, loaded: number, total: number) => {
            onProgress && onProgress(url, loaded, total)
        }

        manager.onLoad = () => {
            onLoad && onLoad();
        }

        return manager;
    })();

    // obj 格式模型的加载器
    objLoader = new OBJLoader(this.loadingManager);

    //fbx 格式模型的加载器
    fbxLoader = new FBXLoader(this.loadingManager);

    //gltf 格式模型的加载器
    gltfLoader = new GLTFLoader(this.loadingManager);

    //纹理加载器
    //TODO loader.setCrossOrigin( "Anonymous");
    textureLoader = new TextureLoader(this.loadingManager);

    // 性能监测工具
    // @ts-ignore
    stats = new Stats();

    //git动态纹理加载器
    // gifTextureLoader = new GifTextureLoader(this.loadingManager);

    // 图片下载
    downimage() {
        const oA = document.createElement("a");
        oA.download = new Date().getTime() + ".png";// 设置下载的文件名，默认是"下载"
        oA.href = this.toImage();
        document.body.appendChild(oA);
        oA.click();
        oA.remove(); // 下载之后把创建的元素删除
    }

    // 返回base64
    toImage() {
        return this.renderer.domElement.toDataURL("image/png").replace("image/png", "image/octet-stream");
    }

    // 渲染场景
    renderScene() {
        const { props } = this;

        // 不传 needRender 和 this.props.needRender=true 时才执行渲染
        if (!this.props.needRender || (this.props.needRender && this.props.needRender())) {

            if (this.isUnMoment) {
                return;
            }
            // 分发事件
            this.mouseEvent.dispatchEvent(this.mouseEventMesh);

            // 为每一个object3d 对象按结构顺序分发渲染前的事件
            this.scene.traverseVisible(object3d => {
                object3d.dispatchEvent({
                    type: Object3DEvent.BEFORE_RENDER,
                    data: {
                        renderer: this.renderer,
                        camera: this.camera,
                        scene: this.scene
                    }
                });
            });

            // 主动更新一次世界矩阵
            this.scene.updateMatrixWorld(true);

            // 渲染之前的回调
            if (props.onBeforeRender) {
                props.onBeforeRender(this.renderer, this.camera, this.scene);
            }

            // 清理画布
            this.renderer.clear();

            // 渲染整个场景
            this.renderer.render(this.scene, this.camera);

            // 绘制碰撞实例模型
            if (this.props.dev) {
                this.renderer.render(this.collideCubeInstanceMesh, this.camera);
                this.renderer.render(this.collideCylindricalInstanceMesh, this.camera);
                this.renderer.render(this.collidePlaneInstanceMesh, this.camera);
            }

            // 渲染后期处理通道
            this.composer.render();

            // 为每一个object3d 对象按结构顺序分发渲染后的事件
            // this.scene.traverseVisible(object3d => {
            //     object3d.dispatchEvent({
            //         type: Object3DEvent.AFTER_RENDER,
            //         data: {
            //             renderer: this.renderer,
            //             camera: this.camera,
            //             scene: this.scene
            //         }
            //     });
            // });

            // props.onAfterRender 
            if (props.onAfterRender) {
                props.onAfterRender(this.renderer, this.camera, this.scene);
            }

            if (props.dev) {
                this.stats.update();

                // 开发模式调试用的 高亮模型
                if (xrt_util.getRoot(reactThreeDevTool.devObjet3d) === this.scene) {
                    this.renderer.clearDepth();
                    this.renderer.render(reactThreeDevTool.devMesh, this.camera);
                }
            }
        }

        // 移动端渲染的时候限制一下帧频率避免手机发烫
        if (this.isMobile) {

            setTimeout(() => {
                xrt_util.requestAnimationFrame(() => this.renderScene(), true);
            }, Math.max(25 - xrt_util.clock.getDelta()));

        } else {

            xrt_util.requestAnimationFrame(() => this.renderScene(), true);
        }
    };

    componentDidUpdate() {
        const {
            dev,
            shadowMapEnabled = false,
            shadowMapType = THREE.BasicShadowMap
        } = this.props;

        // 投影效果开启
        this.renderer.shadowMap.enabled = shadowMapEnabled;
        // 柔滑阴影锯齿
        this.renderer.shadowMap.type = shadowMapType;

        let rootDom = ReactDOM.findDOMNode(this) as HTMLDivElement;

        if (dev) {
            rootDom.appendChild(this.stats.domElement);
        } else {
            this.stats.domElement.remove();
        }
    }

    // 自动调节尺寸
    onresize = () => {
        const { onResize } = this.props;

        let rootDom = ReactDOM.findDOMNode(this) as HTMLDivElement;
        const style = window.getComputedStyle(rootDom);
        let width = parseInt(style.width);
        let height = parseInt(style.height);

        this.camera.aspect = width / height;
        this.camera.updateProjectionMatrix();
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(width, height);

        this.composer.setSize(width, height); // 通道也需要调整尺寸

        if (onResize) {
            onResize(width, height);
        }
    };

    componentDidMount() {
        let rootDom = ReactDOM.findDOMNode(this) as HTMLDivElement;
        rootDom.prepend(this.renderer.domElement);

        this.stats.domElement.style.position = "absolute";

        if (this.props.dev) {
            rootDom.prepend(this.stats.domElement);
        }

        this.onresize();
        xrt_util.requestAnimationFrame(() => this.renderScene(), true);
        xrt_util.requestAnimationFrame(() => this.onresize());
        elementResizeEvent(rootDom, this.onresize);
    }

    componentWillUnmount() {
        this.isUnMoment = true;// 组件已经卸载了

        this.renderer.forceContextLoss();
        this.composer.renderer.dispose();
        this.composer.renderer.properties.dispose()
        this.composer.renderer.renderLists.dispose()
        this.composer.renderTarget1.dispose()
        this.composer.renderTarget1.texture.dispose()
        this.composer.renderTarget2.dispose()
        this.composer.renderTarget2.texture.dispose()
        this.composer.writeBuffer.dispose()
        this.composer.writeBuffer.texture.dispose()
        this.composer.readBuffer.dispose()
        this.composer.readBuffer.texture.dispose()
    }


    render() {

        let { dev, style, className } = this.props;
        return (
            <div style={{ width: 800, height: 400, ...style }} className={className} >
                <ReactThreeContext.Provider
                    value={{
                        dev,
                        emiter: this.emiter,
                        parent: this.scene,
                        scene: this.scene,
                        camera: this.camera,
                        renderer: this.renderer,
                        collideCubeInstanceMesh: this.collideCubeInstanceMesh, // 碰撞检测专用的实例模型
                        collideCylindricalInstanceMesh: this.collideCylindricalInstanceMesh, // 碰撞检测专用的实例模型
                        collidePlaneInstanceMesh: this.collidePlaneInstanceMesh, // 碰撞检测专用的实例模型
                        composer: this.composer,
                        audioListener: this.audioListener,
                        mouseEventMesh: this.mouseEventMesh,
                        loadingManager: this.loadingManager,
                        objLoader: this.objLoader,
                        fbxLoader: this.fbxLoader,
                        gltfLoader: this.gltfLoader,
                        textureLoader: this.textureLoader,
                        TitleDomController: this.TitleDomController,
                        // gifTextureLoader: this.gifTextureLoader
                    }}
                >
                    <TitleDom ref={instance => instance && this.TitleDomController.setTitleDomInstance(instance)} />
                    {this.props.children}
                </ReactThreeContext.Provider>
            </div >
        );
    }
}
